﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MathDrawDll
{
    /// <summary>
	/// 数学函数曲线绘制，使用本类中的方法时，请保证传入的绘图对象，使用系统默认的设备坐标系
	/// </summary>
	//为了让自变量的取值范围里能有小于1的数，自变量单位为厘米
    public class MathDrawClass
    {
		//画笔宽度
		float penWidth = 0.5F;
		//本类的，用直角坐标系标识的绘图区域
		RectangleF rect;
		//从外部传入本类的绘图对象
		Graphics g;
		//构造方法
		public MathDrawClass(Graphics _g, Rectangle _rect)
		{
			g = _g;
			//旧坐标系下的绘图区域中心点
			int oX = (_rect.Left + _rect.Right) / 2;
			int oY = (_rect.Top + _rect.Bottom) / 2;
			//DpiX：当前屏幕上，横轴的每英寸中所包含的像素个数
			//将绘图区域的中心点，在旧坐标系下的位置换算成毫米单位
			float oXF = oX / g.DpiX * 25.4F;
			float oYF = oY / g.DpiY * 25.4F;
			g.PageUnit = GraphicsUnit.Millimeter;
			//设置新坐标系，这里设置为直角坐标系，原点设在绘图区域中心
			Matrix myMatrix = new Matrix(1, 0, 0, -1, oXF, oYF);
			g.Transform = myMatrix;
			//设置绘图质量，抗锯齿参数默认为2
			g.SmoothingMode = SmoothingMode.HighQuality;
			//在直角坐标系下，标识绘图区域
			rect = new RectangleF(-oXF, -oYF, oXF * 2, oYF * 2);
		}
		//绘制坐标轴
		public void DrawFrame()
		{
			Pen myPen = new Pen(Color.Black, penWidth);
			PointF x_left = new PointF(rect.Left, 0);
			PointF x_right = new PointF(rect.Right, 0);
			g.DrawLine(myPen, x_left, x_right);//绘制X轴
			PointF y_down = new PointF(0, rect.Bottom);
			PointF y_up = new PointF(0, rect.Top);
			g.DrawLine(myPen, y_down, y_up);//绘制Y轴

		}
		//绘制 y = x
		public void DrawYEX()
		{
			//样点数组，每2毫米计算一个样点，在X轴上从-4cm到4cm，共41个样点
			PointF[] points = new PointF[41];
			//为了让自变量的取值范围里能有小于1的数，自变量单位为厘米
			float startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				//因坐标系的度量单位是毫米，所以样点坐标设为毫米值
				points[i].X = startXF * 10;
				//计算因变量
				points[i].Y = startXF * 10;
				startXF += 0.2F; //自变量取值增加0.2厘米
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}
		//绘制 y = x ^ 2
		public void DrawYEX2()
		{
			//样点数组，每2毫米计算一个样点，在X轴上从-4cm到4cm，共41个样点
			PointF[] points = new PointF[41];
			//为了让自变量的取值范围里能有小于1的数，自变量单位为厘米
			float startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				//因坐标系的度量单位是毫米，所以样点坐标设为毫米值
				points[i].X = startXF * 10;
				//计算因变量
				points[i].Y = startXF * startXF * 10;
				startXF += 0.2F; //自变量取值增加0.2厘米
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}
		//绘制 y = x ^ 3
		public void DrawYEX3()
		{
			PointF[] points = new PointF[41];
			float startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = startXF * startXF * startXF * 10;
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}
		// y = 1/x 函数的自变量不能为0，所以分成左右两部分绘制
		public void DrawYEXReciprocal()
		{
			PointF[] pointsleft = new PointF[20];
			float startXF = -4.0F;
			for (int i = 0; i < 20; i++)
			{
				pointsleft[i].X = startXF * 10;
				pointsleft[i].Y = 1 / startXF * 10;
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Black, penWidth), pointsleft);

			PointF[] pointsRight = new PointF[20];
			startXF = 0.2F;
			for (int i = 0; i < 20; i++)
			{
				pointsRight[i].X = startXF * 10;
				pointsRight[i].Y = 1 / startXF * 10;
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Black, penWidth), pointsRight);
		}
		// y = log（2(x)） 以2为底的对数函数
		public void DrawLog2()
		{
			PointF[] points = new PointF[800];
			float startXF = 0.01F;
			for (int i = 0; i < 800; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = (float)Math.Log(startXF, 2.0) * 10;//以2为底
				startXF += 0.01F;
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}
		// y = log（e(x)）= ln(x) 以e为底的对数函数
		public void DrawLoge()
		{
			PointF[] points = new PointF[800];
			float startXF = 0.01F;
			for (int i = 0; i < 800; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = (float)Math.Log(startXF) * 10;//以e为底
				startXF += 0.01F;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		// y = e (x)和 y = e (-x),底为e的指数函数
		public void DrawExp()
		{
			PointF[] points = new PointF[41];
			float startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = (float)Math.Exp(startXF) * 10;//底为e的指数函数
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);

			startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = (float)Math.Exp(-startXF) * 10;//y = e (-x)
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		//y = 3 * sin(x) 设振幅为3厘米
		public void DrawSin()
		{
			PointF[] points = new PointF[81];
			//画两个周期，-2PI ～ +2PI
			float startXF = (float)(-2 * Math.PI);
			for (int i = 0; i < 81; i++)
			{
				points[i].X = startXF * 10;
				points[i].Y = (float)Math.Sin(startXF) * 30; //设振幅为3厘米
															 //自变量区间：-2PI ～ +2PI，81个样点，分为80份，每份 4PI / 80
				startXF += (float)(Math.PI / 20);
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}
		//y = tg(x) 正切，画3个正切曲线，以PI为周期
		public void Drawtan()
		{
			float startXF;
			//每段曲线20个样点,分为21份，排除PI/2等特殊点
			float step = (float)Math.PI / 21;
			PointF[] points = new PointF[20];
			int i = 0;
			for (i = -3; i < 3; i += 2)
			{
				//绘制分割线
				g.DrawLine(new Pen(Color.Gray, 0.01F),
					(float)(i * Math.PI / 2) * 10, rect.Top,
					(float)(i * Math.PI / 2) * 10, rect.Bottom);
				//从比（-3/2）PI稍大的地方开始，否则函数值太大
				startXF = (float)(i * Math.PI / 2) + step;
				//每段曲线20个样点,分为21份
				for (int j = 0; j < 20; j++)
				{
					points[j].X = startXF * 10;
					points[j].Y = (float)(Math.Tan(startXF)) * 10;
					startXF += step;
				}
				g.DrawLines(new Pen(Color.Black, penWidth), points);
			}
			g.DrawLine(new Pen(Color.Gray, 0.01F),
				(float)(i * Math.PI / 2) * 10, rect.Top,
				(float)(i * Math.PI / 2) * 10, rect.Bottom);
		}
		//y = K * e(-ax) * sin(x)，指数衰减的正弦曲线，这里令a = 0.1，K = 6
		public void DrawExpSin()
		{
			float a = 0.1F;
			float K = 6;
			PointF[] points = new PointF[200];
			float startXF = 0;
			for (int i = 0; i < 200; i++)
			{
				points[i].X = startXF * 2;//令X坐标在屏幕上增长得缓慢一些
				points[i].Y = (float)Math.Exp(-startXF * a) * (float)Math.Sin(startXF) * K * 10;
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		//y = Sa(x) = sin(x)/x，取样函数，或称辛格函数
		public void DrawSa()
		{
			PointF[] points = new PointF[400];
			float startXF = -40.0F;
			for (int i = 0; i < 400; i++)
			{
				points[i].X = startXF * 2;
				points[i].Y = (float)Math.Sin(startXF) / startXF * 40;
				startXF += 0.2F;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		//心形线1，极坐标：p = a(1 + cos(t)) 或 p = a(1 - cos(t))，水平方向
		//直角坐标：y = p * sin(t), x = p * cos(t)
		public void DrawHeartLine1()
		{
			int a = 3;
			float step = (float)(2 * Math.PI / 360);
			float startAngle = 0;
			PointF[] points = new PointF[360];
			for (int i = 0; i < 360; i++)
			{
				points[i].X = (float)(a * (1 + Math.Cos(startAngle)) * Math.Cos(startAngle)) * 10;
				points[i].Y = (float)(a * (1 + Math.Cos(startAngle)) * Math.Sin(startAngle)) * 10;
				startAngle += step;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		//心形线2，极坐标：p = a(1 - sin(t)) 或 p = a(1 - sin(t))，垂直方向
		//直角坐标：y = p * sin(t), x = p * cos(t)
		public void DrawHeartLine2()
		{
			int a = 3;
			float step = (float)(2 * Math.PI / 360);
			float startAngle = 0;
			PointF[] points = new PointF[360];
			for (int i = 0; i < 360; i++)
			{
				points[i].X = (float)(a * (1 - Math.Sin(startAngle)) * Math.Cos(startAngle)) * 10;
				points[i].Y = (float)(a * (1 - Math.Sin(startAngle)) * Math.Sin(startAngle)) * 10;
				startAngle += step;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
		// 心形线3
		// x = a(2cos(t)-cos(2t))
		// y = a(2sin(t)-sin(2t))
		public void DrawHeartLine3()
		{
			int a = 2;
			float step = (float)(2 * Math.PI / 360);
			float startAngle = 0;
			PointF[] points = new PointF[360];
			for (int i = 0; i < 360; i++)
			{
				points[i].Y = (float)(a * (2 * Math.Cos(startAngle) - Math.Cos(2 * startAngle))) * 10;
				points[i].X = (float)(a * (2 * Math.Sin(startAngle) - Math.Sin(2 * startAngle))) * 10;
				startAngle += step;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}

		//绘制 y = ax^2+bx+c
		public void Draw14(int a,int b,int c)
		{
			//样点数组，每2毫米计算一个样点，在X轴上从-4cm到4cm，共41个样点
			PointF[] points = new PointF[41];
			//为了让自变量的取值范围里能有小于1的数，自变量单位为厘米
			float startXF = -4.0F;
			for (int i = 0; i < 41; i++)
			{
				//因坐标系的度量单位是毫米，所以样点坐标设为毫米值
				points[i].X = startXF * 10;
				//计算因变量
				points[i].Y = (a * (startXF * startXF) + (b * startXF) + c) * 10;
				startXF += 0.2F; //自变量取值增加0.2厘米
			}
			g.DrawLines(new Pen(Color.Black, penWidth), points);
		}

		//螺旋线，极坐标：r = a^t
		//直角坐标：y = p * sin(t), x = p * cos(t)
		public void Draw15(float a)
		{
			float b = 1f;
			float step = (float)(2 * Math.PI / 360);
			float startAngle = 0;
			PointF[] points = new PointF[360];
			for (int i = 0; i < 360; i++)
			{
				points[i].X = (float)((a + (b * startAngle)) * Math.Cos(startAngle)) * 10;
				points[i].Y = (float)((a + (b * startAngle)) * Math.Sin(startAngle)) * 10;
				startAngle += step;
			}
			g.DrawLines(new Pen(Color.Red, penWidth), points);
		}
	}
}
